feat(scan): add 'sbom' to the scan_kind enum (SBOM ingest prereq)#404
Merged
Conversation
Add a third scan_kind value, `sbom`, for the upcoming external CycloneDX SBOM ingest endpoint. Ingested-SBOM scans reuse the Scan model so they get ref-keyed retention, the per-project active-scan guard, and the existing findings/components/licenses UI for free — they just need a distinct kind. - alembic 0032: ALTER TYPE scan_kind ADD VALUE IF NOT EXISTS 'sbom' (forward-only, same transaction-safe idiom as 0030) - SCAN_KIND_VALUES (model binding) and the ScanKind wire Literal updated - parity test asserting the DB enum tuple == the wire Literal, so a value added to one without the other fails CI first This PR is the schema prereq only; the ingest endpoint/task ship separately.
|
|
||
| from alembic import op | ||
|
|
||
| revision: str = "0032" |
| from alembic import op | ||
|
|
||
| revision: str = "0032" | ||
| down_revision: str | None = "0031" |
|
|
||
| revision: str = "0032" | ||
| down_revision: str | None = "0031" | ||
| branch_labels: str | Sequence[str] | None = None |
| revision: str = "0032" | ||
| down_revision: str | None = "0031" | ||
| branch_labels: str | Sequence[str] | None = None | ||
| depends_on: str | Sequence[str] | None = None |
This was referenced Jun 13, 2026
haksungjang
added a commit
that referenced
this pull request
Jun 13, 2026
image-scan kept HARD-failing on lodash 4.17.19 (CVE-2021-23337, CVE-2026-4800) and minimist 1.2.5 (CVE-2021-44906) even after the cdxgen 12.3.3→12.5.1 bump, which only rebuilt the cdxgen layer. A fresh local install of cdxgen 12.5.1 and of npm 11.14.1 — the image's only two npm-package installers — pulls neither package, and these CVEs were never in .trivyignore, yet image-scan passed on #404/#405. The vulnerable copies therefore live in a stale, earlier `scope=worker` cache layer (a non-deterministic npm-install resolution cached long ago), not in anything the current Dockerfile produces. Bumping the buildx GHA cache scope (worker → worker-v2) abandons the poisoned cache and forces a single clean rebuild; the new namespace caches the clean tree. Keeps the cdxgen 12.5.1 bump (latest 12.x, verified lodash/minimist-free).
haksungjang
added a commit
that referenced
this pull request
Jun 14, 2026
…rker image-scan (#407) A no-cache linux/amd64 rebuild of the worker image (image-scan gate) HARD-fails on three node-pkg findings vendored under cdxgen's global install tree: - lodash 4.17.19 CVE-2021-23337 (HIGH), CVE-2026-4800 (HIGH) - minimist 1.2.5 CVE-2021-44906 (CRITICAL) These are pulled by a platform-gated (cpu=x64/os=linux) transitive of cdxgen's dependency graph: a fresh `npm install -g @cyclonedx/cdxgen@12.3.3` on linux/amd64 resolves them, while the same install on arm64/macOS resolves neither — so they were masked by the cached worker layer (image-scan passed on #404/#405) and surfaced only once that GHA cache evicted and CI did a clean amd64 rebuild. It is a pre-existing, main-wide latent issue, unrelated to any one feature PR. Add .trivyignore entries following the file's policy (CVE + target + CVSS + reach analysis + re-evaluate date). All three are UNREACHED: cdxgen is invoked only for dependency enumeration with a fixed argv, never calls lodash.template on scanned-repo input, and the worker never invokes lodash/minimist directly. Re-evaluate when cdxgen ships a fixed vendored tree.
haksungjang
added a commit
that referenced
this pull request
Jun 14, 2026
* feat(scan): external CycloneDX SBOM ingest endpoint
Add POST /v1/projects/{id}/sbom-ingest so external tools (CI, cdxgen-based
scanners) can upload an already-generated CycloneDX SBOM; TRUSCA runs the
back half of the scan pipeline against it — persist components → trivy sbom
matching → findings — reusing the Scan model so ingested scans get ref-keyed
retention, the per-project active-scan guard, and the existing
Components/Vulnerabilities/Licenses UI and build gate for free.
This is NOT a Dependency-Track compatible surface: it is a TRUSCA-native
endpoint (Authorization: Bearer, field `sbom`, no autoCreate), not DT's
/api/v1/bom + X-Api-Key.
Endpoint / service (services/sbom_ingest_service.py, api/v1/sbom.py):
- multipart sbom + ref + release; 202 ScanPublic (kind="sbom").
- require_role_or_api_key("developer"); project-scoped key must match.
- Reuses trigger_scan's guards via an extracted prepare_scan_target
(existence/team 404/403 before archived 409 / cap 429 — authz before state).
- Synchronous adversarial validation of untrusted input: bounded read
(SBOM_INGEST_MAX_BYTES, 32 MiB → 413), content-type/filename allow-list
(415), JSON + CycloneDX structure whitelist (422), component cap
(SBOM_INGEST_MAX_COMPONENTS, 50k → 422), and an O(n) string-aware byte
nesting-depth pre-check so a deeply nested document is a clean 422 instead
of a RecursionError → 500 from json.loads. RFC 7807 throughout.
- Atomic: flush wins the active-scan race before the file is written; a 409
loser writes no file; commit-race deletes the file; enqueue failure → 503.
Celery task (tasks/ingest_sbom.py, enqueue branch + include):
- ingest_sbom_task reuses persist_sbom_components → run_trivy_sbom →
persist_trivy_findings → mark_succeeded (ref-keyed supersede). Preserves the
uploaded SBOM as a durable sbom_cyclonedx ScanArtifact for the signature
surface; containment-guards the path under workspace_root().
Security (Producer-Reviewer findings addressed):
- bind_audit_team before the scan INSERT so the audit row carries team_id.
- disk-write failure → 503 SbomIngestStorageError (retryable), not 422.
- release / original_filename length-capped + control-byte stripped.
Tests: pure adversarial validator unit suite (incl. depth-bomb regression),
endpoint permission×state matrix + new existence-hide-state 409 rows,
realistic multi-CVE fixture pipeline test. Docs: EN/KO ci-integration/sbom-upload.
* test(scan): regenerate OpenAPI snapshot for sbom-ingest endpoint
The OpenAPI contract snapshot test (test_openapi_no_drift) flagged the new
POST /v1/projects/{project_id}/sbom-ingest path. Add it to the committed
snapshot — path param project_id only (sbom/ref/release are requestBody).
* fix(worker): bump cdxgen 12.3.3 → 12.5.1 to bust stale image-scan node-pkg layer
image-scan (worker) HARD-failed on 3 node-pkg findings — lodash 4.17.19
(CVE-2021-23337, CVE-2026-4800) and minimist 1.2.5 (CVE-2021-44906) — that
live under @cyclonedx/cdxgen/node_modules. Reproduction in node:20-bookworm
shows cdxgen 11.x bundles both, while 12.3.3 AND 12.5.1 ship neither: a clean
build already lacks them, so the failure was a stale type=gha scope=worker
cache layer serving the pre-12.x install tree (same class as the earlier
php-symfony image-scan incident).
Bumping the version interpolated into the global npm install changes that
layer's cache key, forcing a fresh (clean) install — root-cause removal, not
a .trivyignore suppression (suppressing a package absent from a clean build
would wrongly mute a future regression). cdxgen invocation is unchanged across
12.3.3→12.5.1 and engines.node still allows ^20, so no scan regression. Fixes
main too (shared cache) once merged.
* fix(ci): bump worker image-scan GHA cache scope to force a clean rebuild
image-scan kept HARD-failing on lodash 4.17.19 (CVE-2021-23337, CVE-2026-4800)
and minimist 1.2.5 (CVE-2021-44906) even after the cdxgen 12.3.3→12.5.1 bump,
which only rebuilt the cdxgen layer. A fresh local install of cdxgen 12.5.1
and of npm 11.14.1 — the image's only two npm-package installers — pulls
neither package, and these CVEs were never in .trivyignore, yet image-scan
passed on #404/#405. The vulnerable copies therefore live in a stale, earlier
`scope=worker` cache layer (a non-deterministic npm-install resolution cached
long ago), not in anything the current Dockerfile produces.
Bumping the buildx GHA cache scope (worker → worker-v2) abandons the poisoned
cache and forces a single clean rebuild; the new namespace caches the clean
tree. Keeps the cdxgen 12.5.1 bump (latest 12.x, verified lodash/minimist-free).
* Revert "fix(ci): bump worker image-scan GHA cache scope to force a clean rebuild"
This reverts commit a17e5fa.
* Revert "fix(worker): bump cdxgen 12.3.3 → 12.5.1 to bust stale image-scan node-pkg layer"
This reverts commit 20a3040.
haksungjang
added a commit
that referenced
this pull request
Jun 14, 2026
…s/SBOM pages (model 3) (#413) The CI-integration how-to (ci-integration/sbom-upload.md) already covered the upload endpoint + conformance verdict, but the product user-guide did not: - user-guide/scans.md: add the 'sbom' scan kind to the kinds table, correct the 'Source/Container only' dialog note (three kinds now; sbom is uploaded, not picked), and add a 'Received SBOMs (uploaded)' section covering formats (CycloneDX/SPDX), the ingest endpoint, and the advisory pass/warn/fail conformance verdict + per-check meaning, cross-linking the CI guide. - user-guide/sbom.md: add an export-vs-upload note distinguishing this page (export from a scan) from uploading a supplier SBOM, linking both surfaces. - EN + KO mirrored; KO translation-style lint S1/S2 clean. (CHANGELOG is batched at release-prep here — #404–#412 will be captured then, matching how the concurrent ingest PRs were handled.)
haksungjang
added a commit
that referenced
this pull request
Jun 14, 2026
…gest + conformance) (#414) Capture the model-3 arc (#404–#413) under a new Keep-a-Changelog [Unreleased] section so the next release-prep PR rolls it into a version: received-SBOM ingest endpoint, SPDX input support, conformance scoring (verdict + API + UI panel), the 'sbom' scan kind, the pipeline-helper extraction, and the docs.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Adds a third
scan_kindvalue —sbom— as the schema prerequisite for an external CycloneDX SBOM ingest endpoint (follow-up PRs).Why
The ingest feature lets external tools (e.g. BomLens) upload an already-generated CycloneDX SBOM; TRUSCA runs
trivy sbomagainst it and persists components/licenses/vulnerabilities. Reusing theScanmodel gives those ingested scans ref-keyed retention, the per-project active-scan guard, and the existing Components/Vulnerabilities/Licenses UI for free — they only need a distinctkind.scan_kindis a native Postgres enum (created in mig 0003), so aScanINSERT withkind='sbom'is rejected at the DB until the type accepts it. Adding the value up front lets the ingest PR ship as pure application code.Changes
ALTER TYPE scan_kind ADD VALUE IF NOT EXISTS 'sbom'. Forward-only (CLAUDE.md §6); same transaction-safe idiom as 0030. Additive — existing rows unaffected.SCAN_KIND_VALUES(SQLAlchemy binding) and theScanKindwire Literal both extended to("source", "container", "sbom").tests/unit/test_catalog_contracts.py) asserting the DB enum tuple == the wire Literal (CLAUDE.md §2 rule 2 — same vocabulary in 2+ places needs a contract test). Pure import, no DB/redis.Scope notes
ScanKindmirror gets'sbom'in the frontend PR.Verification
mypy .(full, incl. tests): clean.ruff checkon changed files: clean.alembic heads: single head0032, linear0031 -> 0032.